/*
 * Authors (alphabetical order)
 * - Andre Bernet <bernet.andre@gmail.com>
 * - Bertrand Songis <bsongis@gmail.com>
 * - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
 * - Cameron Weeks <th9xer@gmail.com>
 * - Erez Raviv
 * - Jean-Pierre Parisy
 * - Karl Szmutny <shadow@privy.de>
 * - Michael Blandford
 * - Michal Hlavinka
 * - Pat Mackenzie
 * - Philip Moss
 * - Rob Thomson
 * - Romolo Manfredini <romolo.manfredini@gmail.com>
 * - Thomas Husterer
 *
 * open9x is based on code named
 * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/,
 * er9x by Erez Raviv: http://code.google.com/p/er9x/,
 * and the original (and ongoing) project by
 * Thomas Husterer, th9x: http://code.google.com/p/th9x/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#ifndef open9x_h
#define open9x_h

#include <inttypes.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>

#if defined(PCBX9D) || defined(PCBACT)
#define IF_9X(x)
#else
#define IF_9X(x) x,
#endif

#if defined(PCBSKY9X)
#define IF_PCBSKY9X(x) x,
#else
#define IF_PCBSKY9X(x)
#endif

#if defined(PCBX9D)
#define IF_PCBX9D()    true
#define CASE_PCBX9D(x) x,
#else
#define IF_PCBX9D()    false
#define CASE_PCBX9D(x)
#endif

#if defined(CPUARM)
#define IF_CPUARM(x) x,
#else
#define IF_CPUARM(x)
#endif

#if defined(RTCLOCK)
#define IF_RTCLOCK(x) x,
#else
#define IF_RTCLOCK(x)
#endif

#if defined(AUDIO)
#define IF_AUDIO(x) x,
#else
#define IF_AUDIO(x)
#endif

#if defined(VOICE)
#define IF_VOICE(x) x,
#else
#define IF_VOICE(x)
#endif

#if defined(PWM_BACKLIGHT)
#define CASE_PWM_BACKLIGHT(x) x,
#else
#define CASE_PWM_BACKLIGHT(x)
#endif

#if defined(VARIO)
#define IF_VARIO(x) x,
#else
#define IF_VARIO(x)
#endif

#if defined(HAPTIC)
#define IF_HAPTIC(x) x,
#else
#define IF_HAPTIC(x)
#endif

#if defined(SPLASH)
#define IF_SPLASH(x) x,
#else
#define IF_SPLASH(x)
#endif

#if defined(FRSKY)
#define IF_FRSKY(x) x,
#else
#define IF_FRSKY(x)
#endif

#if defined(SDCARD)
#define IF_SDCARD(x) x,
#else
#define IF_SDCARD(x)
#endif

#if defined(BLUETOOTH)
#define IF_BLUETOOTH(x) x,
#else
#define IF_BLUETOOTH(x)
#endif

#if defined(HELI)
#define IF_HELI(x) x,
#else
#define IF_HELI(x)
#endif

#if defined(TEMPLATES)
#define IF_TEMPLATES(x) x,
#else
#define IF_TEMPLATES(x)
#endif

#if defined(FLIGHT_PHASES)
#define IF_FLIGHT_PHASES(x) x,
#else
#define IF_FLIGHT_PHASES(x)
#endif

#if defined(CURVES)
#define IF_CURVES(x) x,
#else
#define IF_CURVES(x)
#endif

#if defined(GVARS)
#define IF_GVARS(x) x,
#else
#define IF_GVARS(x)
#endif

#if ROTARY_ENCODERS > 0
#define ROTARY_ENCODER_NAVIGATION
#endif

#if defined(SIMU)
#ifndef FORCEINLINE
#define FORCEINLINE
#endif
#ifndef NOINLINE
#define NOINLINE
#endif
#define CONVERT_PTR(x) ((uint32_t)(uint64_t)(x))
#else
#define FORCEINLINE inline __attribute__ ((always_inline))
#define NOINLINE __attribute__ ((noinline))
#define SIMU_SLEEP(x)
#define CONVERT_PTR(x) ((uint32_t)(x))
#endif

#if defined(PCBACT)
#include "act/board_act.h"
#elif defined(PCBX9D)
#include "x9d/board_x9d.h"
#elif defined(PCBSKY9X)
#include "sky9x/board_sky9x.h"
#elif defined(PCBGRUVIN9X)
#include "gruvin9x/board_gruvin9x.h"
#else
#include "stock/board_stock.h"
#endif

#if defined(CPUARM)
#include "debug.h"
#endif

#if defined(SIMU)
#include "simpgmspace.h"
#elif defined(CPUARM)
typedef const unsigned char pm_uchar;
typedef const char pm_char;
typedef const uint16_t pm_uint16_t;
typedef const uint8_t pm_uint8_t;
typedef const int16_t pm_int16_t;
typedef const int8_t pm_int8_t;
#define pgm_read_byte(address_short) (*(uint8_t*)(address_short))
#define PSTR(adr) adr
#define PROGMEM
#define pgm_read_adr(x) *(x)
#define cli()
#define sei()
extern void boardInit();
#else
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "pgmtypes.h"

#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL  // 16 MHz
#include <util/delay.h>
#define pgm_read_adr(address_short) pgm_read_word(address_short)
#include <avr/wdt.h>
#endif

#define NUM_STICKS    4

#if defined(PCBX9D) || defined(PCBACT)
#define NUM_SWITCHES  8
#define IS_3POS(sw)   ((sw) != 5 && (sw) != 7)
#define MAX_PSWITCH   (SW_SH2-SW_SA0+1)
#define NUM_POTS      4
#define NUM_SW_SRCRAW 8
#else
#define NUM_SWITCHES  7
#define IS_3POS(sw)   ((sw) == 3)
#define MAX_PSWITCH   (SW_TRN-SW_THR+1)  // 9 physical switches
#define NUM_POTS      3
#define NUM_SW_SRCRAW 1
#endif

#include "myeeprom.h"

#if ROTARY_ENCODERS > 0
#define IF_ROTARY_ENCODERS(x) x,
#else
#define IF_ROTARY_ENCODERS(x)
#endif

#define PPM_CENTER 1500

#if defined(PPM_CENTER_ADJUSTABLE)
#define PPM_CH_CENTER(ch) (PPM_CENTER+limitaddress(ch)->ppmCenter)
#else
#define PPM_CH_CENTER(ch) (PPM_CENTER)
#endif

#if defined(CPUARM)
extern char modelNames[MAX_MODELS][sizeof(g_model.name)];
#endif

#if defined(CPUARM)
// This doesn't need protection on this processor
#define tmr10ms_t uint32_t
extern volatile tmr10ms_t g_tmr10ms;
#define get_tmr10ms() g_tmr10ms
#else
#define tmr10ms_t uint16_t
extern volatile tmr10ms_t g_tmr10ms;
extern inline uint16_t get_tmr10ms()
{
  uint16_t time  ;
  cli();
  time = g_tmr10ms ;
  sei();
  return time ;
}
#endif

// TODO try to merge the 2 include files
#if defined(PCBSKY9X)
#include "eeprom_arm.h"
#else
#include "eeprom_avr.h"
#endif

#if defined(CPUARM)
#include "pulses_arm.h"
#else
#include "pulses_avr.h"
#endif

#if defined(PCBX9D)
#define MODEL_BITMAP_WIDTH  64
#define MODEL_BITMAP_HEIGHT 32
#define MODEL_BITMAP_SIZE   (2+4*(MODEL_BITMAP_WIDTH*MODEL_BITMAP_HEIGHT/8))
extern uint8_t modelBitmap[MODEL_BITMAP_SIZE];
void loadModelBitmap();
#define LOAD_MODEL_BITMAP() loadModelBitmap()
#else
#define LOAD_MODEL_BITMAP()
#endif

#if defined(DSM2)
extern bool s_bind_mode;
extern bool s_rangecheck_mode;
extern uint8_t s_bind_allowed;
#endif

#if defined(CPUARM)
#define IS_PPM_PROTOCOL(protocol)     (protocol==PROTO_PPM)
#else
#define IS_PPM_PROTOCOL(protocol)     (protocol<=PROTO_PPMSIM)
#endif

#if defined(PXX)
#define IS_PXX_PROTOCOL(protocol)  (protocol==PROTO_PXX)
#else
#define IS_PXX_PROTOCOL(protocol)  (0)
#endif

#if defined(DSM2)
#define IS_DSM2_PROTOCOL(protocol) (protocol>=PROTO_DSM2_LP45 && protocol<=PROTO_DSM2_DSMX)
#else
#define IS_DSM2_PROTOCOL(protocol) (0)
#endif

#if defined(DSM2_SERIAL)
#define IS_DSM2_SERIAL_PROTOCOL(protocol)  (IS_DSM2_PROTOCOL(protocol))
#else
#define IS_DSM2_SERIAL_PROTOCOL(protocol)  (0)
#endif


#define NUM_PORT1_CHANNELS (IS_PXX_PROTOCOL(g_model.protocol) ? 8 : (IS_DSM2_PROTOCOL(g_model.protocol) ? 6 : (8+(g_model.ppmNCH*2))))
#if defined(PCBSKY9X)
#define NUM_PORT2_CHANNELS (8+(g_model.ppm2NCH*2))
#endif

#include "lcd.h"
#include "menus.h"
#ifdef TEMPLATES
#include "templates.h"
#endif

#if !defined(SIMU)
#define assert(x)
#if !defined(CPUARM) || !defined(DEBUG)
#define printf printf_not_allowed
#endif
#endif

extern const pm_uint8_t bchout_ar[];
extern const pm_uint8_t modn12x3[];

extern uint8_t stickMode;

//convert from mode 1 to mode stickMode
//NOTICE!  =>  1..4 -> 1..4
#define CONVERT_MODE(x)  (((x)<=4) ? pgm_read_byte(modn12x3 + 4*stickMode + (x)-1) : (x) )

#define RUD_STICK 0
#define ELE_STICK 1
#define THR_STICK 2
#define AIL_STICK 3

extern uint8_t channel_order(uint8_t x);

enum EnumKeys {
  KEY_MENU,
  KEY_EXIT,
#if defined(PCBACT)
  KEY_CLR,
  KEY_PAGE,
  KEY_PLUS,  /* Fake, used for rotary encoder */
  KEY_MINUS, /* Fake, used for rotary encoder */
#elif defined(PCBX9D)
  KEY_ENTER,
  KEY_PAGE,
  KEY_PLUS,
  KEY_MINUS,
#else
  KEY_DOWN,
  KEY_UP,
  KEY_RIGHT,
  KEY_LEFT,
#endif
  TRM_LH_DWN,
  TRM_LH_UP,
  TRM_LV_DWN,
  TRM_LV_UP,
  TRM_RV_DWN,
  TRM_RV_UP,
  TRM_RH_DWN,
  TRM_RH_UP,

#if ROTARY_ENCODERS > 0 || defined(ROTARY_ENCODER_NAVIGATION)
  BTN_REa,
#endif
#if ROTARY_ENCODERS > 0
  BTN_REb,
#endif

  NUM_KEYS,
  SW_BASE=NUM_KEYS,

  //SW_NC,
  //SW_ON,
#if defined(PCBX9D) || defined(PCBACT)
  SW_SA0=SW_BASE,
  SW_SA1,
  SW_SA2,
  SW_SB0,
  SW_SB1,
  SW_SB2,
  SW_SC0,
  SW_SC1,
  SW_SC2,
  SW_SD0,
  SW_SD1,
  SW_SD2,
  SW_SE0,
  SW_SE1,
  SW_SE2,
  SW_SF0,
  SW_SF2,
  SW_SG0,
  SW_SG1,
  SW_SG2,
  SW_SH0,
  SW_SH2,
#else
  SW_THR=SW_BASE,
  SW_RUD,
  SW_ELE,
  SW_ID0,
  SW_ID1,
  SW_ID2,
#if 0
  SW_ID3,
  SW_ID4,
  SW_ID5,
#endif
  SW_AIL,
  SW_GEA,
  SW_TRN,
#endif

  SW_SW1,
  SW_SW2,
  SW_SW3,
  SW_SW4,
  SW_SW5,
  SW_SW6,
  SW_SW7,
  SW_SW8,
  SW_SW9,
  SW_SWA,
  SW_SWB,
  SW_SWC,
};

#define DSW(x)   (1+(x)-SW_BASE)

#if defined(PCBACT)
#define KEY_ENTER  BTN_REa
#define KEY_RIGHT  KEY_PLUS
#define KEY_LEFT   KEY_MINUS
#define KEY_UP     KEY_MINUS
#define KEY_DOWN   KEY_PLUS
#elif defined(PCBX9D)
#define KEY_RIGHT  KEY_PLUS
#define KEY_LEFT   KEY_MINUS
#define KEY_UP     KEY_PLUS
#define KEY_DOWN   KEY_MINUS
#else
#define KEY_ENTER  KEY_MENU
#define KEY_PLUS   KEY_RIGHT
#define KEY_MINUS  KEY_LEFT
#endif

#if defined(PCBX9D) || defined(PCBACT)
/* mapping of 9x switches */
#define SW_THR     SW_SA2
#define SW_RUD     SW_SB2
#define SW_ELE     SW_SC2
#define SW_ID0     SW_SD0
#define SW_ID1     SW_SD1
#define SW_ID2     SW_SD2
#define SW_AIL     SW_SF2
#define SW_GEA     SW_SG2
#define SW_TRN     SW_SH2
#endif

class Key
{
#define FILTERBITS      4
#ifdef SIMU
#define FFVAL 1
#else
#define FFVAL          ((1<<FILTERBITS)-1)
#endif
#define KSTATE_OFF      0
#define KSTATE_RPTDELAY 95 // gruvin: delay state before key repeating starts
#define KSTATE_START    97
#define KSTATE_PAUSE    98
#define KSTATE_KILLED   99
  uint8_t m_vals;   // key debounce?  4 = 40ms
  uint8_t m_cnt;
  uint8_t m_state;
public:
  void input(bool val, EnumKeys enuk);
  bool state()       { return m_vals > 0; }
  void pauseEvents() { m_state = KSTATE_PAUSE; m_cnt = 0; }
  void killEvents()  { m_state = KSTATE_KILLED; }
};

extern Key keys[NUM_KEYS];

enum BaseCurves {
  CURVE_NONE,
  CURVE_X_GT0,
  CURVE_X_LT0,
  CURVE_ABS_X,
  CURVE_F_GT0,
  CURVE_F_LT0,
  CURVE_ABS_F,
  CURVE_BASE
};

#define SWASH_TYPE_120   1
#define SWASH_TYPE_120X  2
#define SWASH_TYPE_140   3
#define SWASH_TYPE_90    4
#define SWASH_TYPE_NUM   4

enum CswFunctions {
  CS_OFF,
  // TODO add CS_VEQUAL,
  CS_VPOS,   // v>offset
  CS_VNEG,   // v<offset
  CS_APOS,   // |v|>offset
  CS_ANEG,   // |v|<offset
  CS_AND,
  CS_OR,
  CS_XOR,
  CS_EQUAL,
  CS_NEQUAL, // TODO remove
  CS_GREATER,
  CS_LESS,
  CS_EGREATER, // TODO remove
  CS_ELESS, // TODO remove
  CS_DIFFEGREATER,
  CS_ADIFFEGREATER,
  // TODO add CS_TIMER,
  CS_MAXF = CS_ADIFFEGREATER
};

#define CS_VOFS       0
#define CS_VBOOL      1
#define CS_VCOMP      2
#define CS_VDIFF      3
#define CS_STATE(x)   ((x)<CS_AND ? CS_VOFS : ((x)<CS_EQUAL ? CS_VBOOL : ((x)<CS_DIFFEGREATER ? CS_VCOMP : CS_VDIFF)))

#define MAX_SWITCH    (MAX_PSWITCH+NUM_CSW)
#define SWITCH_ON     (1+MAX_SWITCH)
#define SWITCH_OFF    (-SWITCH_ON)

#define NUM_CYC         3
#if defined(PCBX9D) || defined(PCBACT)
#define CSW_PPM_BASE    24 // TODO garbage to compile ...
#else
#define CSW_PPM_BASE    (MIXSRC_3POS+NUM_CYC) // because srcRaw is shifted +1!
#endif
#define NUM_CAL_PPM     4
#define NUM_PPM         8
#define CSW_CHOUT_BASE  (CSW_PPM_BASE+NUM_PPM)

#if defined(FRSKY_HUB)
#define NUM_TELEMETRY      TELEM_CSW_MAX
#elif defined(WS_HOW_HIGH)
#define NUM_TELEMETRY      TELEM_ALT
#elif defined(FRSKY)
#define NUM_TELEMETRY      TELEM_A2
#elif defined(MAVLINK)
// Number sw position
#define NUM_TELEMETRY      4
#define ROTARY_SW_CHANNEL "UP  DOWN"
// Channel number for rotary switch
//#define MIX_SW_ROLL_CHAN (CSW_CHOUT_BASE+NUM_CHNOUT+NUM_VIRTUAL) // GVA:Rotary switch
#define MIX_INC_ROTARY_SW (CSW_CHOUT_BASE+NUM_CHNOUT+MAX_TIMERS+1)
#define MIX_DEC_ROTARY_SW (CSW_CHOUT_BASE+NUM_CHNOUT+MAX_TIMERS+1)
#else
#define NUM_TELEMETRY      TELEM_TM2
#endif

#define NUM_XCHNRAW  (NUM_STICKS+NUM_POTS+NUM_ROTARY_ENCODERS+NUM_STICKS+1/*MAX*/+NUM_SW_SRCRAW+NUM_CYC+NUM_PPM+NUM_CHNOUT)

#if defined(PCBX9D) || defined(PCBACT)
#define NUM_XCHNMIX  (NUM_XCHNRAW+NUM_CSW)
#define NUM_XCHNCSW  (NUM_XCHNRAW+NUM_CSW+NUM_TELEMETRY)
#define NUM_XCHNPLAY (NUM_XCHNRAW+NUM_CSW+TELEM_DISPLAY_MAX)
#else
#define NUM_XCHNMIX  (NUM_XCHNRAW+MAX_SWITCH)
#define NUM_XCHNCSW  (NUM_XCHNRAW+NUM_TELEMETRY)
#define NUM_XCHNPLAY (NUM_XCHNRAW+TELEM_DISPLAY_MAX)
#endif

#define THRCHK_DEADBAND 16

#if defined(FSPLASH) || defined(XSPLASH)
#define SPLASH_TIMEOUT  (g_eeGeneral.splashMode == 0 ? 60000/*infinite=10mn*/ : ((4*100) * (g_eeGeneral.splashMode & 0x03)))
#else
#define SPLASH_TIMEOUT  (4*100)  // 4 seconds
#endif

#define TRM_BASE TRM_LH_DWN

#define EVT_KEY_MASK(e)    ((e) & 0x0f)

#define _MSK_KEY_BREAK     0x20
#define _MSK_KEY_REPT      0x40
#define _MSK_KEY_FIRST     0x60
#define _MSK_KEY_LONG      0x80

#define EVT_KEY_BREAK(key) ((key)|_MSK_KEY_BREAK)
#define EVT_KEY_FIRST(key) ((key)|_MSK_KEY_FIRST)
#define EVT_KEY_REPT(key)  ((key)|_MSK_KEY_REPT)
#define EVT_KEY_LONG(key)  ((key)|_MSK_KEY_LONG)

#define IS_KEY_BREAK(evt)  (((evt)&0xf0) ==  _MSK_KEY_BREAK)
#define IS_KEY_FIRST(evt)  (((evt)&0xf0) ==  _MSK_KEY_FIRST)
#define IS_KEY_LONG(evt)   (((evt)&0xf0) ==  _MSK_KEY_LONG)

#define EVT_ENTRY          0xbf
#define EVT_ENTRY_UP       0xbe

#if defined(PCBX9D)
#define EVT_ROTARY_BREAK   EVT_KEY_BREAK(KEY_ENTER)
#define EVT_ROTARY_LONG    EVT_KEY_LONG(KEY_ENTER)
#else
#define EVT_ROTARY_BREAK   0xcf
#define EVT_ROTARY_LONG    0xce
#define EVT_ROTARY_LEFT    0xdf
#define EVT_ROTARY_RIGHT   0xde
#endif

#if defined(PCBX9D)
#define IS_ROTARY_LEFT(evt)   (evt==EVT_KEY_FIRST(KEY_MOVE_DOWN) || evt==EVT_KEY_REPT(KEY_MOVE_DOWN))
#define IS_ROTARY_RIGHT(evt)  (evt==EVT_KEY_FIRST(KEY_MOVE_UP) || evt==EVT_KEY_REPT(KEY_MOVE_UP))
#define IS_ROTARY_BREAK(evt)  (evt==EVT_KEY_BREAK(KEY_ENTER))
#define IS_ROTARY_LONG(evt)   (evt==EVT_KEY_LONG(KEY_ENTER))
#define IS_ROTARY_EVENT(evt)  (0)
#define CASE_EVT_ROTARY_BREAK /*case EVT_KEY_BREAK(KEY_ENTER):*/
#define CASE_EVT_ROTARY_LONG  /*case EVT_KEY_LONG(KEY_ENTER):*/
#define CASE_EVT_ROTARY_LEFT  case EVT_KEY_FIRST(KEY_MOVE_DOWN): case EVT_KEY_REPT(KEY_MOVE_DOWN):
#define CASE_EVT_ROTARY_RIGHT case EVT_KEY_FIRST(KEY_MOVE_UP): case EVT_KEY_REPT(KEY_MOVE_UP):
#elif defined(ROTARY_ENCODER_NAVIGATION)
#define IS_ROTARY_LEFT(evt)   (evt == EVT_ROTARY_LEFT)
#define IS_ROTARY_RIGHT(evt)  (evt == EVT_ROTARY_RIGHT)
#define IS_ROTARY_BREAK(evt)  (evt == EVT_ROTARY_BREAK)
#define IS_ROTARY_LONG(evt)   (evt == EVT_ROTARY_LONG)
#define IS_ROTARY_EVENT(evt)  (EVT_KEY_MASK(evt) >= 0x0e)
#define CASE_EVT_ROTARY_BREAK case EVT_ROTARY_BREAK:
#define CASE_EVT_ROTARY_LONG  case EVT_ROTARY_LONG:
#define CASE_EVT_ROTARY_LEFT  case EVT_ROTARY_LEFT:
#define CASE_EVT_ROTARY_RIGHT case EVT_ROTARY_RIGHT:
#else
#define IS_ROTARY_LEFT(evt)  (0)
#define IS_ROTARY_RIGHT(evt) (0)
#define IS_ROTARY_BREAK(evt) (0)
#define IS_ROTARY_LONG(evt)  (0)
#define IS_ROTARY_EVENT(evt) (0)
#define CASE_EVT_ROTARY_BREAK
#define CASE_EVT_ROTARY_LONG
#define CASE_EVT_ROTARY_LEFT
#define CASE_EVT_ROTARY_RIGHT
#endif

#if defined(PCBX9D)
  #define IS_RE_NAVIGATION_ENABLE()   true
  #define NAVIGATION_RE_IDX()         0
#elif defined(ROTARY_ENCODERS)
  #define NAVIGATION_RE_IDX()         (g_eeGeneral.reNavigation - 1)
  #define IS_RE_NAVIGATION_ENABLE()   g_eeGeneral.reNavigation
#elif defined(ROTARY_ENCODER_NAVIGATION)
  #define IS_RE_NAVIGATION_ENABLE()   true
  #define NAVIGATION_RE_IDX()         0
#endif


#define HEART_TIMER_PULSES  1
#define HEART_TIMER10ms     2
extern uint8_t heartbeat;

#define MAX_ALERT_TIME   60

extern uint8_t inacPrescale;
extern uint16_t inacCounter;
extern uint16_t inacSum;

#if defined(PXX)
extern uint8_t pxxFlag;
#endif

#define PXX_SEND_RXNUM     0x01
#define PXX_SEND_FAILSAFE  0x02

#define ZCHAR_MAX (40 + LEN_SPECIAL_CHARS)

extern char idx2char(int8_t idx);

void clearKeyEvents();
void pauseEvents(uint8_t enuk);
void killEvents(uint8_t enuk);
#if defined(CPUARM)
uint8_t getEvent(bool trim);
#else
uint8_t getEvent();
#endif
void putEvent(uint8_t evt);

uint8_t keyDown();

enum PowerState {
  e_power_on,
  e_power_trainer,
  e_power_usb,
  e_power_off
};

#if defined(CPUARM)
uint32_t switchState(EnumKeys enuk);
#else
bool switchState(EnumKeys enuk);
#if defined(PCBGRUVIN9X)
uint8_t pwrCheck();
#else
#define pwrCheck() (e_power_on)
#endif
#endif

void readKeysAndTrims();

uint16_t evalChkSum();

#if defined(VOICE)
#define MESSAGE_SOUND_ARG , uint8_t sound
#define MESSAGE(title, msg, info, sound) message(title, msg, info, sound)
#define ALERT(title, msg, sound) alert(title, msg, sound)
#else
#define MESSAGE_SOUND_ARG
#define MESSAGE(title, msg, info, sound) message(title, msg, info)
#define ALERT(title, msg, sound) alert(title, msg)
#endif

extern void message(const pm_char *title, const pm_char *s, const char *last MESSAGE_SOUND_ARG);
extern void alert(const pm_char * t, const pm_char * s MESSAGE_SOUND_ARG);

enum PerOutMode {
  e_perout_mode_normal = 0,
  e_perout_mode_notrainer = 1,
  e_perout_mode_notrims = 2,
  e_perout_mode_nosticks = 4,
  e_perout_mode_noinput = e_perout_mode_notrainer+e_perout_mode_notrims+e_perout_mode_nosticks
};

// Fiddle to force compiler to use a pointer
#define FORCE_INDIRECT(ptr) __asm__ __volatile__ ("" : "=e" (ptr) : "0" (ptr))

extern uint8_t s_perout_flight_phase;

#if defined(CPUARM)
#define bitfield_channels_t uint32_t
#else
#define bitfield_channels_t uint16_t
#endif

void perOut(uint8_t mode, uint8_t tick10ms);
void perMain();
NOINLINE void per10ms();

int16_t getValue(uint8_t i);
bool    getSwitch(int8_t swtch, bool nc);

extern swstate_t switches_states;
int8_t  getMovedSwitch();

#ifdef FLIGHT_PHASES
extern uint8_t getFlightPhase();
#else
#define getFlightPhase() 0
#endif

extern uint8_t getTrimFlightPhase(uint8_t phase, uint8_t idx);
extern int16_t getRawTrimValue(uint8_t phase, uint8_t idx);
extern int16_t getTrimValue(uint8_t phase, uint8_t idx);
extern void setTrimValue(uint8_t phase, uint8_t idx, int16_t trim);

#if defined(ROTARY_ENCODERS)
int16_t getRotaryEncoder(uint8_t idx);
void incRotaryEncoder(uint8_t idx, int8_t inc);
#endif

#if defined(PCBGRUVIN9X)
#define ROTARY_ENCODER_GRANULARITY 1
#else
#define ROTARY_ENCODER_GRANULARITY 2
#endif

#if defined(GVARS)
  #if defined(CPUM64)
    int16_t getGVarValue(int16_t x, int16_t min, int16_t max);
    void setGVarValue(uint8_t x, int8_t value);
    #define GET_GVAR(x, min, max, p) getGVarValue(x, min, max)
    #define SET_GVAR(idx, val, p) setGVarValue(idx, val)
  #else
    uint8_t getGVarFlightPhase(uint8_t phase, uint8_t idx);
    int16_t getGVarValue(int16_t x, int16_t min, int16_t max, int8_t phase);
    void setGVarValue(uint8_t x, int8_t value, int8_t phase);
    #define GET_GVAR(x, min, max, p) getGVarValue(x, min, max, p)
    #define SET_GVAR(idx, val, p) setGVarValue(idx, val, p)
  #endif
  #define GV1_SMALL  123
  #define GV1_LARGE  1024
  #define GVAR_DISPLAY_TIME     100 /*1 second*/;
  extern uint8_t s_gvar_timer;
  extern uint8_t s_gvar_last;
#else
  #define GET_GVAR(x, ...) (x)
#endif

extern uint16_t s_timeCumTot;
extern uint16_t s_timeCumThr;  //gewichtete laufzeit in 1/16 sec
extern uint16_t s_timeCum16ThrP; //gewichtete laufzeit in 1/16 sec
extern uint8_t  s_timerState[2];
extern int16_t  s_timerVal[2];
extern uint8_t  s_timerVal_10ms[2];
extern int8_t safetyCh[NUM_CHNOUT];

extern uint8_t trimsCheckTimer;

#define TMR_OFF     0
#define TMR_RUNNING 1
#define TMR_BEEPING 2
#define TMR_STOPPED 3
void resetTimer(uint8_t idx);
void resetAll();

extern uint8_t unexpectedShutdown;
extern uint8_t g_tmr1Latency_max;
extern uint8_t g_tmr1Latency_min;
extern uint16_t maxMixerDuration;
extern uint16_t lastMixerDuration;

#if defined(THRTRACE)
#define MAXTRACE (LCD_W - 8)
extern uint8_t s_traceBuf[MAXTRACE];
extern uint8_t s_traceWr;
extern int8_t s_traceCnt;
#endif

#if defined(PCBX9D) || defined(PCBACT)
static inline uint16_t getTmr2MHz() { return 0; }
#elif defined(PCBSKY9X)
static inline uint16_t getTmr2MHz() { return TC1->TC_CHANNEL[0].TC_CV; }
#else
uint16_t getTmr16KHz();
#endif

#if defined(CPUARM)
uint16_t stack_free(uint8_t tid);
#else
uint16_t stack_free();
#endif

#if defined(CPUM64)
void memclear(void *ptr, uint8_t size);
#else
#define memclear(p, s) memset(p, 0, s)
#endif

#if defined(SPLASH)
void doSplash();
#endif

void checkLowEEPROM();
void checkTHR();
void checkSwitches();
void checkAlarm();
void checkAll();

#define ADC_VREF_TYPE 0x40 // AVCC with external capacitor at AREF pin

#if !defined(SIMU)
void getADC();
#endif

#define STORE_MODELVARS eeDirty(EE_MODEL)
#define STORE_GENERALVARS eeDirty(EE_GENERAL)

extern void backlightOn();

enum Analogs {
  STICK1,
  STICK2,
  STICK3,
  STICK4,
#if defined(PCBX9D)
  POT1,
  POT2,
  SLIDER1,
  SLIDER2,
#else
  POT1,
  POT2,
  POT3,
#endif
  TX_VOLTAGE,
#if defined(PCBSKY9X) && !defined(REVA)
  TX_CURRENT,
#endif
  NUMBER_ANALOG
};

#if defined(PCBSTD) && defined(VOICE) && !defined(SIMU)
#define BACKLIGHT_ON()    (Voice.Backlight = 1)
#define BACKLIGHT_OFF()   (Voice.Backlight = 0)
#else
#define BACKLIGHT_ON()    __BACKLIGHT_ON
#define BACKLIGHT_OFF()   __BACKLIGHT_OFF
#endif

#define BUZZER_ON     PORTE |=  (1 << OUT_E_BUZZER)
#define BUZZER_OFF    PORTE &= ~(1 << OUT_E_BUZZER)

#if defined(HAPTIC)
#if defined(PCBSKY9X)
#define HAPTIC_OFF    hapticOff()
#elif defined(PCBGRUVIN9X)
#define HAPTIC_ON     PORTD &= ~(1 << OUT_D_HAPTIC)
#define HAPTIC_OFF    PORTD |=  (1 << OUT_D_HAPTIC)
#else
#define HAPTIC_ON     PORTG |=  (1 << OUT_G_HAPTIC)
#define HAPTIC_OFF    PORTG &= ~(1 << OUT_G_HAPTIC)
#endif
#else
#define HAPTIC_ON
#define HAPTIC_OFF
#endif

#define BITMASK(bit) (1<<(bit))

/// liefert Dimension eines Arrays
#define DIM(arr) (sizeof((arr))/sizeof((arr)[0]))

/// liefert Betrag des Arguments
template<class t> FORCEINLINE t abs(t a) { return a>0?a:-a; }
/// liefert das Minimum der Argumente
template<class t> FORCEINLINE t min(t a, t b) { return a<b?a:b; }
/// liefert das Maximum der Argumente
template<class t> FORCEINLINE t max(t a, t b) { return a>b?a:b; }
template<class t> FORCEINLINE t sgn(t a) { return a>0 ? 1 : (a < 0 ? -1 : 0); }
template<class t> FORCEINLINE t limit(t mi, t x, t ma) { return min(max(mi,x),ma); }

#if defined(HELI) || defined(FRSKY_HUB)
uint16_t isqrt32(uint32_t n);
#endif

#if defined(CPUARM)
#if !defined(SIMU)
extern "C" {
#include <CoOS.h>
}
#endif

extern OS_MutexID mixerMutex;
inline void pauseMixerCalculations()
{
  CoEnterMutexSection(mixerMutex);
}

inline void resumeMixerCalculations()
{
  CoLeaveMutexSection(mixerMutex);
}
#else
#define pauseMixerCalculations()
#define resumeMixerCalculations()
#endif

#if defined(CPUARM) || defined(PCBGRUVIN9X)
void saveTimers();
#else
#define saveTimers()
#endif

void eeDirty(uint8_t msk);
void eeCheck(bool immediately=false);
void eeReadAll();
bool eeModelExists(uint8_t id);
void eeLoadModelName(uint8_t id, char *name);
void eeLoadModel(uint8_t id);
void generalDefault();
void modelDefault(uint8_t id);

#if defined(CPUARM)
inline int16_t calc100to256(register int8_t x)  // @@@2 open.20.fsguruh: return x*2.56
{
  return ((int16_t) x * 256) / 100;
}

inline int16_t calc100toRESX_16Bits(register int16_t x) // @@@ open.20.fsguruh
{
  return x * 1024 / 100;
}

inline int32_t calc100toRESX(register int8_t x)
{
  return x * 1024 / 100;
}

inline int16_t calc1000toRESX(register int32_t x)  // improve calc time by Pat MacKenzie
{
  return x * 1024 / 1000;
}

inline int16_t calcRESXto1000(register int32_t x)
{
  return x * 1000 / 1024;
}

inline int16_t calcRESXto100(register int32_t x)
{
  return x * 100 / 1024;
}

#else
extern int16_t calc100to256(int8_t x); // @@@2 open.20.fsguruh: return x*2.56
extern int16_t calc100toRESX_16Bits(int16_t x); // @@@ open.20.fsguruh
extern int16_t calc100toRESX(int8_t x);
extern int16_t calc1000toRESX(int16_t x);
extern int16_t calcRESXto1000(int16_t x);
#endif

#define TMR_VAROFS  5

extern const char stamp1[];
extern const char stamp2[];
extern const char stamp3[];
extern const char eeprom_stamp[];

extern uint8_t            g_vbat100mV;
#define g_blinkTmr10ms (*(uint8_t*)&g_tmr10ms)
extern uint8_t            g_beepCnt;
extern uint8_t            g_beepVal[5];

extern uint8_t            ppmInState; //0=unsync 1..8= wait for value i-1
extern int16_t            g_ppmIns[8];
extern int32_t            chans[NUM_CHNOUT];
extern int16_t            ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of the last perMain
extern int16_t            g_chans512[NUM_CHNOUT];
extern uint16_t           BandGap;

extern uint16_t expou(uint16_t x, uint16_t k);
extern int16_t expo(int16_t x, int16_t k);
extern int16_t intpol(int16_t, uint8_t);
extern int16_t applyCurve(int16_t, int8_t);
extern void applyExpos(int16_t *anas);
extern int16_t applyLimits(uint8_t channel, int32_t value);

extern uint16_t anaIn(uint8_t chan);
extern int16_t thrAnaIn(uint8_t chan);
extern int16_t calibratedStick[NUM_STICKS+NUM_POTS];

#define FLASH_DURATION 50 /*500ms*/

extern uint8_t beepAgain;
extern uint16_t lightOffCounter;
extern uint8_t mixWarning;

extern PhaseData *phaseaddress(uint8_t idx);
extern ExpoData *expoaddress(uint8_t idx);
extern MixData *mixaddress(uint8_t idx);
extern LimitData *limitaddress(uint8_t idx);
extern int8_t *curveaddress(uint8_t idx);
extern CustomSwData *cswaddress(uint8_t idx);

struct CurveInfo {
  int8_t *crv;
  uint8_t points;
  bool custom;
};
extern CurveInfo curveinfo(uint8_t idx);

extern void deleteExpoMix(uint8_t expo, uint8_t idx);

extern void incSubtrim(uint8_t idx, int16_t inc);
extern void instantTrim();
extern void moveTrimsToOffsets();

#if defined(CPUARM)
#define ACTIVE_EXPOS_TYPE uint32_t
#define ACTIVE_MIXES_TYPE uint64_t
#define ACTIVE_PHASES_TYPE uint16_t
#else
#define ACTIVE_EXPOS_TYPE uint16_t
#define ACTIVE_MIXES_TYPE uint32_t
#define ACTIVE_PHASES_TYPE uint8_t
#endif

#ifdef BOLD_FONT
extern ACTIVE_EXPOS_TYPE activeExpos;
extern ACTIVE_MIXES_TYPE activeMixes;
inline bool isExpoActive(uint8_t expo)
{
  return activeExpos & ((ACTIVE_EXPOS_TYPE)1 << expo);
}

inline bool isMixActive(uint8_t mix)
{
  return activeMixes & ((ACTIVE_MIXES_TYPE)1 << mix);
}
#else
#define isExpoActive(x) false
#define isMixActive(x) false
#endif

#if defined(CPUARM)
#define MASK_CFN_TYPE uint32_t // current max = 32 function switches
#define MASK_FUNC_TYPE uint32_t // current max = 32 functions
#else
#define MASK_CFN_TYPE uint16_t // current max = 16 function switches
#define MASK_FUNC_TYPE uint16_t // current max = 16 functions
#endif

extern MASK_CFN_TYPE  activeSwitches;
extern MASK_CFN_TYPE  activeFnSwitches;
extern MASK_FUNC_TYPE activeFunctions;
inline bool isFunctionActive(uint8_t func)
{
  return activeFunctions & ((MASK_FUNC_TYPE)1 << (func-FUNC_TRAINER));
}

#if defined(CPUARM)
typedef int32_t rotenc_t;
#else
typedef int8_t rotenc_t;
#endif

#if defined(ROTARY_ENCODERS)
// Global rotary encoder registers
extern volatile rotenc_t g_rotenc[ROTARY_ENCODERS];
#elif defined(ROTARY_ENCODER_NAVIGATION)
extern volatile rotenc_t g_rotenc[1];
#endif

#ifdef JETI
// Jeti-DUPLEX Telemetry
#include "jeti.h"
#endif

#if defined (FRSKY)
// FrSky Telemetry
#include "frsky.h"
#endif

#ifdef ARDUPILOT
// ArduPilot Telemetry
#include "ardupilot.h"
#endif

#ifdef NMEA
// NMEA Telemetry
#include "nmea.h"
#endif

#ifdef MAVLINK
// Mavlink Telemetry
#include "rotarysw.h"
#include "mavlink.h"
#endif

// REPEAT uses 0x01 to 0x0f
#define PLAY_NOW                  0x10
#define PLAY_BACKGROUND           0x20

/* make sure the defines below always go in numeric order */
enum AUDIO_SOUNDS {
    AU_INACTIVITY,
    AU_TX_BATTERY_LOW,
#if defined(CPUARM)
    AU_TX_MAH_HIGH,
    AU_TX_TEMP_HIGH,
#endif
#if defined(VOICE)
    AU_THROTTLE_ALERT,
    AU_SWITCH_ALERT,
    AU_BAD_EEPROM,
    AU_EEPROM_FORMATTING,
#endif
    AU_ERROR,
    AU_KEYPAD_UP,
    AU_KEYPAD_DOWN,
    AU_MENUS,
    AU_TRIM_MOVE,
    AU_WARNING1,
    AU_WARNING2,
    AU_WARNING3,
    AU_TRIM_MIDDLE,
#if defined(CPUARM)
    AU_TRIM_END,
#endif
    AU_TADA,
    AU_POT_STICK_MIDDLE,
    AU_MIX_WARNING_1,
    AU_MIX_WARNING_2,
    AU_MIX_WARNING_3,
    AU_TIMER_LT3,
    AU_TIMER_10,
    AU_TIMER_20,
    AU_TIMER_30,
    AU_FRSKY_FIRST,
    AU_FRSKY_BEEP1 = AU_FRSKY_FIRST,
    AU_FRSKY_BEEP2,
    AU_FRSKY_BEEP3,
    AU_FRSKY_WARN1,
    AU_FRSKY_WARN2,
    AU_FRSKY_CHEEP,
    AU_FRSKY_RING,
    AU_FRSKY_SCIFI,
    AU_FRSKY_ROBOT,
    AU_FRSKY_CHIRP,
    AU_FRSKY_TADA,
    AU_FRSKY_CRICKET,
    AU_FRSKY_SIREN,
    AU_FRSKY_ALARMC,
    AU_FRSKY_RATATA,
    AU_FRSKY_TICK,
    AU_FRSKY_LAST,

    AU_NONE=0xff
};

#if defined(AUDIO)
#if defined(CPUARM)
#include "audio_arm.h"
#else
#include "audio_avr.h"
#endif
#else
#include "beeper.h"
#endif

#if defined(PCBSTD) && defined(VOICE)
#include "stock/voice.h"
#endif

#if defined(PCBGRUVIN9X) && defined(VOICE)
#include "gruvin9x/somo14d.h"
#endif

#include "translations.h"

#if defined(HAPTIC)
#include "haptic.h"
#endif

#if defined(SDCARD)
#include "sdcard.h"
#endif

#if defined(RTCLOCK)
#include "rtc.h"
#endif

// Re-useable byte array to save having multiple buffers
#define SD_SCREEN_FILE_LENGTH (32)
union ReusableBuffer
{
    /* 128 bytes on stock */

#if !defined(PCBSKY9X)
    uint8_t eefs_buffer[BLOCKS];           // 128bytes used by EeFsck
#endif

    struct
    {
        char mainname[42];
        char listnames[LCD_LINES-1][LEN_MODEL_NAME];
        uint16_t eepromfree;

#if defined(SDCARD)
        char menu_bss[MENU_MAX_LINES][MENU_LINE_LENGTH];
#endif

    } models;                                     // 128bytes used by menuModelSelect (mainname reused for SD card archive / restore)

    struct
    {
        int16_t midVals[NUM_STICKS+NUM_POTS];
        int16_t loVals[NUM_STICKS+NUM_POTS];
        int16_t hiVals[NUM_STICKS+NUM_POTS];
    } calib;                                      // 42 bytes used by menuGeneralCalib

#if defined(SDCARD)
    struct
    {
        char lines[LCD_LINES-1][SD_SCREEN_FILE_LENGTH+1+1]; // the last char is used to store the flags (directory) of the line
        uint32_t available;
        uint16_t offset;
        uint16_t count;
    } sd;
#endif
};

extern union ReusableBuffer reusableBuffer;

void checkFlashOnBeep();

#if defined(FRSKY) || defined(CPUARM)
void putsTelemetryValue(xcoord_t x, uint8_t y, lcdint_t val, uint8_t unit, uint8_t att);
#endif

#if defined(CPUARM)
uint8_t zlen(const char *str, uint8_t size);
char * strcat_zchar(char * dest, char * name, uint8_t size, const char *defaultName, uint8_t defaultNameSize, uint8_t defaultIdx);
#define strcat_modelname(dest, idx) strcat_zchar(dest, modelNames[idx], LEN_MODEL_NAME, STR_MODEL, PSIZE(TR_MODEL), idx+1)
#define strcat_phasename(dest, idx) strcat_zchar(dest, g_model.phaseData[idx].name, LEN_FP_NAME, NULL, 0, 0)
#define strcat_mixername(dest, idx) strcat_zchar(dest, g_model.mixData[idx].name, LEN_EXPOMIX_NAME, NULL, 0, 0)
#define ZLEN(s) zlen(s, sizeof(s))
#endif

// Stick tolerance varies between transmitters, Higher is better
#if defined (PCB9XR) || defined (PCB9XR128)
  #define STICK_TOLERANCE 16
#else
  #define STICK_TOLERANCE 64
#endif

#endif

